/**
 * Copyright 2014-2020  the original author or authors.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.upb.webserver.message;

import com.upb.webserver.common.enums.MessageStatusEnum;
import com.upb.webserver.common.enums.MessageTypeEnum;
import com.upb.webserver.common.exception.WebServerException;
import com.upb.webserver.common.pojo.base.BasePageResponse;
import com.upb.webserver.common.pojo.message.RspMessageRecipientVO;
import com.upb.webserver.common.returncode.ConstantCode;
import com.upb.webserver.common.tools.JsonUtils;
import com.upb.webserver.common.tools.MybatisExampleTools;
import com.upb.webserver.dao.entity.TbMessage;
import com.upb.webserver.dao.entity.TbMessageRecipient;
import com.upb.webserver.dao.entity.TbMessageRecipientExample;
import com.upb.webserver.dao.mapper.TbMessageMapper;
import com.upb.webserver.dao.mapper.TbMessageRecipientMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * services for message data.
 */
@Slf4j
@Service
public class MessageService {

    @Autowired
    private TbMessageMapper messageMapper;
    @Autowired
    private TbMessageRecipientMapper messageRecipientMapper;


    /**
     * new alert message.
     *
     * @param platform
     * @param recipients
     * @param content
     */
    public void addWarnMessage(byte platform, List<Integer> recipients, String content) {
        log.info("start exec method [addWarnMessage]. platform:{} recipients:{} content:{}", platform, JsonUtils.objToString(recipients), content);
        this.addMessage(platform, recipients, content, MessageTypeEnum.WARN.getId());

    }

    /**
     * new inform message
     *
     * @param platform
     * @param recipients
     * @param content
     */
    public void addInfoMessage(byte platform, List<Integer> recipients, String content) {
        log.info("start exec method [addInfoMessage]. platform:{} recipient:{} content:{}", platform, JsonUtils.objToString(recipients), content);
        this.addMessage(platform, recipients, content, MessageTypeEnum.INFO.getId());
    }


    /**
     * addMessage
     *
     * @param platform
     * @param recipients
     * @param content
     * @param type
     * @throws WebServerException
     */
    @Transactional
    public void addMessage(byte platform, List<Integer> recipients, String content, byte type) throws WebServerException {
        log.info("start exec method [addMessage]. platform:{} recipient:{} content:{} type:{}", platform, JsonUtils.objToString(recipients), content, type);

        try {
            //save message
            TbMessage tbMessage = new TbMessage();
            tbMessage.setContent(content);
            tbMessage.setGmtCreate(new Date());
            tbMessage.setGmtModified(new Date());
            tbMessage.setType(type);
            messageMapper.insertSelective(tbMessage);

            //save recipient
            recipients.stream().forEach(r -> ((MessageService) AopContext.currentProxy()).addMessageRecipient(tbMessage.getId(), platform, r));
            log.info("success exec method [addMessage]");
        } catch (Exception ex) {
            log.error("error exec method [addMessage]", ex);
        }
    }

    /**
     * addMessageRecipient
     *
     * @param messageId
     * @param platform
     * @param recipients
     */
    @Transactional
    public void addMessageRecipient(int messageId, byte platform, int recipients) {
        log.info("start exec method [addMessageRecipient]. messageId:{} platform:{} recipients:{}", messageId, platform, recipients);

        TbMessageRecipient tbMessageRecipient = new TbMessageRecipient();
        tbMessageRecipient.setMessageId(messageId);
        tbMessageRecipient.setRecipient(recipients);
        tbMessageRecipient.setPlatform(platform);
        tbMessageRecipient.setGmtCreate(new Date());
        tbMessageRecipient.setGmtModified(new Date());
        messageRecipientMapper.insertSelective(tbMessageRecipient);
        log.info("success exec method [addMessageRecipient]. messageId:{} platform:{} recipients:{}", messageId, platform, recipients);

    }


    /**
     * @param idList
     * @param targetStatus
     */
    public void updateStatus(List<Integer> idList, Byte targetStatus) {
        log.info("start exec method [updateStatus]. idList:{} targetStatus:{}", JsonUtils.objToString(idList), targetStatus);

        //check status
        if (MessageStatusEnum.HAVE_READ.getId() != targetStatus) {
            log.warn("not support this message status:{}", targetStatus);
            throw new WebServerException(ConstantCode.INVALID_MESSAGE_STATUS);
        }

        //param
        TbMessageRecipientExample example = new TbMessageRecipientExample();
        TbMessageRecipientExample.Criteria criteria = example.createCriteria();
        criteria.andIdIn(idList);

        //query
        List<TbMessageRecipient> list = messageRecipientMapper.selectByExample(example);
        if (list == null || list.size() == 0) {
            log.info("fail exec method [updateStatus]. not found record by idList:{} ", JsonUtils.objToString(idList));
            return;
        }

        //update
        list.stream().forEach(record -> {
            record.setStatus(targetStatus);
            messageRecipientMapper.updateByPrimaryKey(record);
        });

        log.info("success exec method [updateStatus]");
    }


    /**
     * query by page.
     *
     * @param pageNumber
     * @param pageSize
     * @return
     */
    public BasePageResponse queryMessageByPage(byte platform, int recipient, Byte status, int pageNumber, int pageSize) {
        log.info("start exec method [queryMessageByPage]. platform:{} recipient:{}  pageNumber:{}  pageSize:{}", platform, recipient, pageNumber, pageSize);
        BasePageResponse pageResponse = BasePageResponse.initialize();
        //query param
        TbMessageRecipientExample example = MybatisExampleTools.initSamplePageExample(TbMessageRecipientExample.class, pageNumber, pageSize, new TbMessageRecipient(platform, recipient, status));
        pageResponse.setTotalCount(messageRecipientMapper.countByExample(example));
        if (pageResponse.getTotalCount() > 0) {
            example.setOrderByClause("id DESC");
            //query
            List<TbMessageRecipient> messageRecipients = messageRecipientMapper.selectByExample(example);
            if (messageRecipients != null && messageRecipients.size() > 0) {
                List<Integer> messageIdList = messageRecipients.stream().map(m -> m.getMessageId()).collect(Collectors.toList());
                List<TbMessage> messages = messageMapper.listByIds(messageIdList);

                //convert to RspMessageRecipientVO
                List<RspMessageRecipientVO> list = new ArrayList<>(messageRecipients.size());
                for (TbMessageRecipient messageRecipient : messageRecipients) {
                    RspMessageRecipientVO obj = new RspMessageRecipientVO();
                    BeanUtils.copyProperties(messageRecipient, obj);
                    messages.stream()
                            .filter(m -> m.getId().equals(messageRecipient.getMessageId()))
                            .findFirst()
                            .ifPresent(mf -> {
                                obj.setMessageContent(mf.getContent());
                                obj.setType(mf.getType());
                            });
                    list.add(obj);
                }

                list.stream().sorted();
                pageResponse.setData(list);
            }
        }

        log.info("success exec method [queryMessageByPage]. result:{}", JsonUtils.objToString(pageResponse));
        return pageResponse;
    }

    /**
     * @param platform
     * @param recipient
     * @return
     */
    public long countUnReadMessage(byte platform, int recipient) {
        TbMessageRecipientExample example = MybatisExampleTools.initSampleExample(TbMessageRecipientExample.class, new TbMessageRecipient(platform, recipient, MessageStatusEnum.UNREAD.getId()));
        return messageRecipientMapper.countByExample(example);
    }

}
